package logica;

import java.awt.geom.Point2D;
import java.util.LinkedList;

/**
 * De klasse Dier is de abstracte bovenklasse van Orka en Boid.
 * Hierin wordt gemeenschappelijke functionaliteit geïmplementeerd.
 * <p>
 * Een dier beweegt zich voort aan een constante snelheid.
 * @see Boid
 * @see Orka
 *
 * @author Bart Bruynooghe
 * @author Benjamin De Clercq
 * @author Thomas Flamant
 * @author Sander Van den Broecke
 */
public abstract class Dier {

	// dataleden
	protected Point2D.Double positie;
	protected double richting;
	protected Zee myZee;
	protected static final int ONTWIJKOBSTAKEL = 20 + Constanten.ZIJDEGEBIED;
	protected boolean zwemslag = false;

	// constructor
	/**
	 * De constructor initialiseert de attributen myZee en positie
	 * @param myZee    De zee van het Dier
	 * @param positie  De positie van het Dier
	 * @see    Boid
	 * @see    Orka
	 */
	public Dier(Zee myZee, Point2D.Double positie, double richting) {
		this.myZee = myZee;
		this.positie = positie;
		this.richting = richting;
	}

	/**
	 * In deze methode worden onder ander obstakels en orka’s ontweken.
	 * Eveneens wordt de eigen positie aangepast in deze methode.
	 * De methode zal geïmplementeerd moeten worden in de klassen die deze
	 * abstracte bovenklassen realiseren
	 * @see    Boid
	 * @see    Orka
	 */
	public abstract void update();

	/**
	 * Geeft de huidige positie terug
	 * @see    Boid
	 * @see    Orka
	 * @return Point de huidige positie van het Dier
	 */
	public Point2D.Double getPositie() {
		return positie;
	}

	public double getRichting() {
		return richting;
	}

	public boolean getZwemslag() {
		return zwemslag;
	}

	public void zwem() {
		zwemslag = !zwemslag;
	}

	/**
	 * Abstractie methode die ingevuld wordt
	 * in elk van de afgeleide klassen Orka en Boid
	 */
	protected abstract void verplaats();

	public double berekenHoek(Point2D.Double obstakel) {
		double hoek = 0.0;
		try {
			double x = obstakel.x - positie.x;
			double y = obstakel.y - positie.y;
			hoek = Math.atan2(y, x);
			if (hoek < 0) {
				hoek = 2 * Math.PI + hoek;
			}
		} catch (Exception ex) {
		}

		return hoek;
	}

	//returnt true als hij iets aan het ontwijken is
	protected boolean ontwijkObstakels() {
		Obstakel teOntwijkenObstakel = null;
		// omliggende obstakels opvragen via Zee (geeft LinkedList met alle gebieden)
		LinkedList<Obstakel> obstakels = myZee.getOmliggendeObstakels(positie);
		for (Obstakel obstakel : obstakels) {
			if (obstakel.getAfstand(positie) < ONTWIJKOBSTAKEL) {
				double hoek = berekenHoek(obstakel.getMiddelPunt());
				if (Math.abs(richting - hoek) <= Math.PI / 2) {
					if (teOntwijkenObstakel == null ||
							teOntwijkenObstakel.getAfstand(positie) > obstakel.getAfstand(positie)) {
						teOntwijkenObstakel = obstakel;
					}
				}
			}
		}
		if (teOntwijkenObstakel != null) {
			// links en rechts uitwijken
			double hoek = berekenHoek(teOntwijkenObstakel.getMiddelPunt());
			double draai = 0.2;

			if (richting - hoek < 0) {
				richting -= draai;
				if (teOntwijkenObstakel.getAfstand(positie) <= Constanten.ZIJDEGEBIED) {
					while (richting - berekenHoek(teOntwijkenObstakel.getMiddelPunt()) > -Math.PI / 2) {
						richting -= draai;
					}
				}
			} else {
				richting += draai;
				if (teOntwijkenObstakel.getAfstand(positie) <= Constanten.ZIJDEGEBIED) {
					while (richting - berekenHoek(teOntwijkenObstakel.getMiddelPunt()) < Math.PI / 2) {
						richting += draai;
					}
				}
			}

			if (richting < 0) {
				richting += 2 * Math.PI;
			} else {
				richting %= 2 * Math.PI;
			}

			if (positie.x <= 11 * Constanten.ZIJDEGEBIED / 10) {
				while (richting >= Math.PI / 2 - draai && richting < Math.PI) {
					richting -= draai;
				}
				while (richting <= 3 * Math.PI / 2 + draai && richting > Math.PI) {
					richting += draai;
				}
			}
			if (positie.x >= Constanten.ZIJDEGEBIED * (Constanten.AANTALGEBIEDENBREEDTE - 1) - Constanten.ZIJDEGEBIED / 10) {
				while (richting >= 3 * Math.PI / 2 - draai && richting < 2 * Math.PI) {
					richting -= draai;
				}
				while (richting <= Math.PI + draai / 2 && richting > 0) {
					richting += draai;
				}
			}
			if (positie.y <= 11 * Constanten.ZIJDEGEBIED / 10) {
				while (richting >= Math.PI - draai && richting < 3 * Math.PI / 2) {
					richting -= draai;
				}
				while (richting <= 2 * Math.PI + draai && richting > 3 * Math.PI / 2) {
					richting += draai;
				}
			}
			if (positie.y >= Constanten.ZIJDEGEBIED * (Constanten.AANTALGEBIEDENHOOGTE - 1) - Constanten.ZIJDEGEBIED / 10) {
				while (richting >= 0 && richting - draai < Math.PI / 2) {
					richting -= draai;
				}
				while (richting <= Math.PI + draai && richting > Math.PI / 2) {
					richting += draai;
				}
			}
		}
		return (teOntwijkenObstakel != null);
	}

	/**
	 * De methode toString() wordt overschreven zodat deze
	 * de positie teruggeeft voor controledoeleinden
	 * @return
	 */
	@Override
	public String toString() {
		return positie.toString();
	}
}
